package com.xyht.sca_s.student_manage_system.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xyht.sca_s.student_manage_system.auth.entity.AuthorizationRelationResp;
import com.xyht.sca_s.student_manage_system.auth.entity.ResourceSimpleResp;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonCodeEnum;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonResult;
import com.xyht.sca_s.student_manage_system.common.commonReturn.ExceptionCast;
import com.xyht.sca_s.student_manage_system.modules.org.entity.SmsOrgUserRelation;
import com.xyht.sca_s.student_manage_system.modules.org.mapper.SmsOrgUserRelationMapper;
import com.xyht.sca_s.student_manage_system.modules.student.entity.SmsStudentInfo;
import com.xyht.sca_s.student_manage_system.modules.student.mapper.SmsStudentInfoMapper;
import com.xyht.sca_s.student_manage_system.modules.teacher.entity.SmsTeacherInfo;
import com.xyht.sca_s.student_manage_system.modules.teacher.mapper.SmsTeacherInfoMapper;
import com.xyht.sca_s.student_manage_system.modules.user.entity.*;
import com.xyht.sca_s.student_manage_system.modules.user.mapper.*;
import com.xyht.sca_s.student_manage_system.modules.visitor.entity.SmsVisitorUser;
import com.xyht.sca_s.student_manage_system.modules.visitor.mapper.SmsVisitorUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.xyht.sca_s.student_manage_system.auth.constant.CommonConstant.*;
import static com.xyht.sca_s.student_manage_system.modules.user.constant.AppUpDownConstant.APP_UP;
import static com.xyht.sca_s.student_manage_system.modules.user.constant.TeacherAndStudentFlag.TEACHER_FLAG;

/**
 * @author dxn
 * @date 2021-08-15 18:44
 */
@Slf4j
@Component
public class CacheUtil {
    private StringRedisTemplate redisTemplate;

    @Autowired
    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    private SmsUserMapper smsUserMapper;

    @Autowired
    public void setSmsUserMapper(SmsUserMapper smsUserMapper) {
        this.smsUserMapper = smsUserMapper;
    }

    @Autowired
    private SmsTeacherInfoMapper smsTeacherInfoMapper;
    @Autowired
    private SmsStudentInfoMapper smsStudentInfoMapper;
    @Autowired
    private SmsUserRoleRelationMapper smsUserRoleRelationMapper;
    @Autowired
    private SmsRoleResourceRelationMapper smsRoleResourceRelationMapper;
    @Autowired
    private SmsResourceMapper smsResourceMapper;
    @Autowired
    private SmsOrgUserRelationMapper smsOrgUserRelationMapper;
    @Autowired
    private SmsRoleApplicationRelationMapper smsRoleApplicationRelationMapper;
    @Autowired
    private SmsApplicationMapper smsApplicationMapper;
    @Autowired
    private SmsVisitorUserMapper smsVisitorUserMapper;


    private final String DEFAULT_KEY_PREFIX = "";
    private final int EXPIRE_TIME = 1;
    private final TimeUnit EXPIRE_TIME_TYPE = TimeUnit.DAYS;

    /**
     *
     * @param roleId 角色id
     * @return 角色下的用户id
     */
    public List<String> getUserListByRoleId(String roleId){
        return smsUserRoleRelationMapper.selectList(new LambdaQueryWrapper<SmsUserRoleRelation>()
                .eq(SmsUserRoleRelation::getRoleId, roleId))
                .stream()
                .map(SmsUserRoleRelation::getUserId).collect(Collectors.toList());

    }

    /**
     *
     * @param orgId 组织id
     * @return 组织下的用户id
     */
    public List<String> getUserListByOrgId(String orgId){
        return smsOrgUserRelationMapper.selectList(new LambdaQueryWrapper<SmsOrgUserRelation>()
                .eq(SmsOrgUserRelation::getOrgId, orgId))
                .stream()
                .map(SmsOrgUserRelation::getUserId).collect(Collectors.toList());
    }

    public List<String> getRoleList(String user_id) {
        String roleKey = ROLE_HEADER + user_id;
        List<String> roleList;
        roleList = getList(roleKey, String.class);
        if (roleList == null || roleList.size() < 1) {
            saveRoleListAndUserInfo(user_id);
            roleList = getList(roleKey, String.class);
        }
        return roleList;
    }

    public List<String> getOrgList(String user_id) {
        String orgKey = ORG_HEADER + user_id;
        List<String> orgList;
        orgList = getList(orgKey, String.class);
        if (orgList == null || orgList.size() < 1) {
            saveOrgListAndUserInfo(user_id);
            orgList = getList(orgKey, String.class);
        }
        return orgList;
    }

    public SmsUser getUserInfo(String user_id) {
        String userKey = USER_HEADER + user_id;
        SmsUser user;
        user = getObject(userKey, SmsUser.class);
        if (user == null || user.getId() == null) {
            saveUserInfo(user_id);
            user = getObject(userKey, SmsUser.class);
        }
        return user;
    }

    public SmsVisitorUser getVisitorUserInfo(String user_id) {
        String userKey = USER_VISITOR_HEADER + user_id;
        SmsVisitorUser user;
        user = getObject(userKey, SmsVisitorUser.class);
        if (user == null || user.getId() == null) {
            saveVisitorUserInfo(user_id);
            user = getObject(userKey, SmsVisitorUser.class);
        }
        return user;
    }

    public List<ResourceSimpleResp> getAuthAll() {
        String authAllKey =AUTH_ALL_HEADER;
        List<ResourceSimpleResp> list;
        list = getList(authAllKey, ResourceSimpleResp.class);
        if (list == null || list.size()<1) {
            saveAllAuth();
            list = getList(authAllKey, ResourceSimpleResp.class);
        }
        return list;
    }

    public List<AuthorizationRelationResp> getUserAuth(String user_id) {
        String authKey = AUTH_HEADER + user_id;
        List<AuthorizationRelationResp> authList;
        authList = getList(authKey, AuthorizationRelationResp.class);
        if (authList == null || authList.size()<1) {
            saveUserAuthorization(user_id);
            authList = getList(authKey, AuthorizationRelationResp.class);
        }
        return authList;
    }

    public List<SmsApplication> getAppNameList(String user_id,Integer client) {
        String appNameKey = APP_NAME_HEADER + client+"_"+user_id;
        List<SmsApplication> appNameList;
        appNameList = getList(appNameKey, SmsApplication.class);
        if (appNameList == null || appNameList.size()<1) {
            saveUserAppList(user_id,client);
            appNameList = getList(appNameKey, SmsApplication.class);
        }
        return appNameList;
    }

    public void saveUserAuthorization(String user_id) {
        SmsUser user = smsUserMapper.selectById(user_id);
        if (user == null) {
            ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.USER_NOT_EXIST));
        }
        String userKey = USER_HEADER + user_id;
        add(userKey, user,3,TimeUnit.MINUTES);
        List<SmsUserRoleRelation> list = smsUserRoleRelationMapper.selectList(new QueryWrapper<SmsUserRoleRelation>()
                .lambda().eq(SmsUserRoleRelation::getUserId, user_id));
        List<String> roleList = list.stream().map(SmsUserRoleRelation::getRoleId).collect(Collectors.toList());
        if (roleList.size() > 0) {
            String roleKey = ROLE_HEADER + user_id;
            add(roleKey, roleList,10,TimeUnit.MINUTES);
        }
        //TODO 角色权限
        List<AuthorizationRelationResp> authList = new ArrayList<>();
        if (roleList.size() > 0) {
            List<SmsRoleResourceRelation> resList = smsRoleResourceRelationMapper.selectList(new QueryWrapper<SmsRoleResourceRelation>()
                    .lambda().in(SmsRoleResourceRelation::getRoleId,roleList));
            authList = resList.stream().map(res -> {
                AuthorizationRelationResp authorizationRelationResp = new AuthorizationRelationResp();
                BeanUtils.copyProperties(res,authorizationRelationResp);
                return authorizationRelationResp;
            }).collect(Collectors.toList());
        }

        //TODO 单独个人权限


        String authKey = AUTH_HEADER+user_id;
        add(authKey,authList,1,TimeUnit.MINUTES);
    }



    public void saveRoleListAndUserInfo(String user_id) {
        SmsUser user = smsUserMapper.selectById(user_id);
        if (user == null) {
            ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.USER_NOT_EXIST));
        }
        String userKey = USER_HEADER + user_id;
        add(userKey, user,1,TimeUnit.MINUTES);
        List<SmsUserRoleRelation> list = smsUserRoleRelationMapper.selectList(new QueryWrapper<SmsUserRoleRelation>()
                .lambda().eq(SmsUserRoleRelation::getUserId, user_id));
        List<String> roleList = list.stream().map(SmsUserRoleRelation::getRoleId).collect(Collectors.toList());
        if (roleList.size() > 0) {
            String roleKey = ROLE_HEADER + user_id;
            add(roleKey, roleList,1,TimeUnit.MINUTES);
        }
    }

    public void saveOrgListAndUserInfo(String user_id) {
        SmsUser user = smsUserMapper.selectById(user_id);
        if (user == null) {
//            ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.USER_NOT_EXIST));
            return;
        }
        String userKey = USER_HEADER + user_id;
        add(userKey, user,1,TimeUnit.MINUTES);
        List<SmsOrgUserRelation> list = smsOrgUserRelationMapper.selectList(new QueryWrapper<SmsOrgUserRelation>()
                .lambda().eq(SmsOrgUserRelation::getUserId, user_id));
        List<String> orgList = list.stream().map(SmsOrgUserRelation::getOrgId).collect(Collectors.toList());
        if (orgList.size() > 0) {
            String orgKey = ORG_HEADER + user_id;
            add(orgKey, orgList,1,TimeUnit.MINUTES);
        }
    }

    public void saveUserInfo(String user_id) {
        SmsUser user = smsUserMapper.selectById(user_id);
        if (user == null) {
//            ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.USER_NOT_EXIST));
            return;
        }
        String userKey = USER_HEADER + user_id;
        add(userKey, user,5,TimeUnit.SECONDS);
    }

    public void saveVisitorUserInfo(String user_id) {
        SmsVisitorUser user = smsVisitorUserMapper.selectById(user_id);
        if (user == null) {
//            ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.USER_NOT_EXIST));
            return;
        }
        String userKey = USER_VISITOR_HEADER + user_id;
        add(userKey, user,1,TimeUnit.MINUTES);
    }

    public void saveAllAuth() {
        List<SmsResource> list = smsResourceMapper.selectList(new QueryWrapper<>());
        List<ResourceSimpleResp> resList = list.stream().map(smsResource -> {
            ResourceSimpleResp resourceSimpleResp = new ResourceSimpleResp();
            BeanUtils.copyProperties(smsResource,resourceSimpleResp);
            return resourceSimpleResp;
        }).collect(Collectors.toList());
        add(AUTH_ALL_HEADER, resList,1,TimeUnit.MINUTES);
    }

    public void saveUserAppList(String user_id,Integer app_client){
        List<String> roleList = this.getRoleList(user_id);
        List<String> appList = new ArrayList<>();
        if (roleList.size()>0){
            List<SmsRoleApplicationRelation> roleApplicationRelationList = smsRoleApplicationRelationMapper.selectList(
                    new QueryWrapper<SmsRoleApplicationRelation>()
                            .lambda().select(SmsRoleApplicationRelation::getAppId).in(SmsRoleApplicationRelation::getRoleId,roleList).groupBy(SmsRoleApplicationRelation::getAppId));
            appList = roleApplicationRelationList.stream().map(SmsRoleApplicationRelation::getAppId).collect(Collectors.toList());
        }
        QueryWrapper<SmsApplication> queryWrapper = new QueryWrapper<>();
        if (appList.size()>0){
            queryWrapper.lambda().in(SmsApplication::getId,appList);
        }
        queryWrapper.lambda().eq(SmsApplication::getAppClient,app_client)
                .eq(SmsApplication::getUpDown,APP_UP);
        List<SmsApplication> smsApplicationList = smsApplicationMapper.selectList(queryWrapper);
        List<String> appNameList = smsApplicationList.stream().map(SmsApplication::getAppName).collect(Collectors.toList());
        String appNameKey = APP_NAME_HEADER + app_client+"_"+user_id;
        add(appNameKey, smsApplicationList,10,TimeUnit.SECONDS);
    }


    public void generateUserCheck(String userId, String methodName) {
        if (userId == null) {
            userId = "";
        }
        if (methodName == null) {
            methodName = "";
        }
        String result = userId + "_" + methodName;
        add(result, 1, 1, TimeUnit.SECONDS);
    }

    public boolean userCheck(String userId, String methodName) {
        if (userId == null) {
            return true;
        }
        if (methodName == null) {
            return true;
        }
        String result = userId + "_" + methodName;
        boolean bl = hasKey(result);
        if (!bl) {
            generateUserCheck(userId, methodName);
        }
        return bl;
    }

    public SmsUser getCatchUserInfo(String user_id) {
        String userKey = USER_HEADER + user_id;
        if (user_id == null || user_id.equals("")) {
            return null;
        }
        SmsUser user = this.getObject(userKey, SmsUser.class);
        if (user == null) {
            user = smsUserMapper.selectById(user_id);
            if (user == null) {
                return null;
            } else {
                this.add(userKey, user, 1, TimeUnit.MINUTES);
            }
        }
        return user;
    }

    public <K, V> V getTeacherOrStudentInfo(String key, Integer flag, Class<V> clazz) {
        String value = this.get(key);
        V result = null;
        if (!StringUtils.isEmpty(value)) {
            result = JSONObject.parseObject(value, clazz);
        } else {
            if (flag.equals(TEACHER_FLAG)) {
                SmsTeacherInfo teacherInfo = smsTeacherInfoMapper.selectById(key);
                if (teacherInfo == null) {
                    return null;
                }
                this.add(key, teacherInfo, 1, TimeUnit.MINUTES);
                result = (V) teacherInfo;
            } else {
                SmsStudentInfo studentInfo = smsStudentInfoMapper.selectById(key);
                if (studentInfo == null) {
                    return null;
                }
                this.add(key, studentInfo, 1, TimeUnit.MINUTES);
                result = (V) studentInfo;
            }
        }
        return result;
    }

    /**
     * 数据缓存至redis
     *
     * @param key
     * @param value
     * @return
     */
    public <K, V> void add(K key, V value) {
        try {
            if (value != null) {
                redisTemplate
                        .opsForValue()
                        .set(DEFAULT_KEY_PREFIX + key, JSON.toJSONString(value));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException("数据缓存至redis失败");
        }
    }

    /**
     * 数据缓存至redis并设置过期时间
     *
     * @param key
     * @param value
     * @return
     */
    public <K, V> void add(K key, V value, long timeout, TimeUnit unit) {
        try {
            if (value != null) {
                redisTemplate
                        .opsForValue()
                        .set(DEFAULT_KEY_PREFIX + key, JSON.toJSONString(value), timeout, unit);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException("数据缓存至redis失败");
        }
    }

    /**
     * 写入 hash-set,已经是key-value的键值，不能再写入为hash-set
     *
     * @param key    must not be {@literal null}.
     * @param subKey must not be {@literal null}.
     * @param value  写入的值
     */
    public <K, SK, V> void addHashCache(K key, SK subKey, V value) {
        redisTemplate.opsForHash().put(DEFAULT_KEY_PREFIX + key, subKey, value);
    }

    /**
     * 写入 hash-set,并设置过期时间
     *
     * @param key    must not be {@literal null}.
     * @param subKey must not be {@literal null}.
     * @param value  写入的值
     */
    public <K, SK, V> void addHashCache(K key, SK subKey, V value, long timeout, TimeUnit unit) {
        redisTemplate.opsForHash().put(DEFAULT_KEY_PREFIX + key, subKey, value);
        redisTemplate.expire(DEFAULT_KEY_PREFIX + key, timeout, unit);
    }

    /**
     * 获取 hash-setvalue
     *
     * @param key    must not be {@literal null}.
     * @param subKey must not be {@literal null}.
     */
    public <K, SK> Object getHashCache(K key, SK subKey) {
        return redisTemplate.opsForHash().get(DEFAULT_KEY_PREFIX + key, subKey);
    }


    /**
     * 从redis中获取缓存数据，转成对象
     *
     * @param key   must not be {@literal null}.
     * @param clazz 对象类型
     * @return
     */
    public <K, V> V getObject(K key, Class<V> clazz) {
        String value = this.get(key);
        V result = null;
        if (!StringUtils.isEmpty(value)) {
            result = JSONObject.parseObject(value, clazz);
        }
        return result;
    }

    /**
     * 从redis中获取缓存数据，转成list
     *
     * @param key   must not be {@literal null}.
     * @param clazz 对象类型
     * @return
     */
    public <K, V> List<V> getList(K key, Class<V> clazz) {
        String value = this.get(key);
        List<V> result = Collections.emptyList();
        if (!StringUtils.isEmpty(value)) {
            result = JSONArray.parseArray(value, clazz);
        }
        return result;
    }

    /**
     * 功能描述：Get the value of {@code key}.
     *
     * @param key must not be {@literal null}.
     * @return java.lang.String
     * @date 2021/9/19
     **/
    public <K> String get(K key) {
        String value;
        try {
            value = redisTemplate.opsForValue().get(DEFAULT_KEY_PREFIX + key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException("从redis缓存中获取缓存数据失败");
        }
        return value;
    }

    /**
     * 删除key
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 批量删除key
     */
    public void delete(Collection<String> keys) {
        redisTemplate.delete(keys);
    }

    /**
     * 序列化key
     */
    public byte[] dump(String key) {
        return redisTemplate.dump(key);
    }

    /**
     * 是否存在key
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 设置过期时间
     */
    public Boolean expireAt(String key, Date date) {
        return redisTemplate.expireAt(key, date);
    }


    /**
     * 移除 key 的过期时间，key 将持久保持
     */
    public Boolean persist(String key) {
        return redisTemplate.persist(key);
    }

    /**
     * 返回 key 的剩余的过期时间
     */
    public Long getExpire(String key, TimeUnit unit) {
        return redisTemplate.getExpire(key, unit);
    }

    /**
     * 返回 key 的剩余的过期时间
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }
}


